package com.ruoyi.framework.web.exception

import com.ruoyi.common.constant.HttpStatus
import com.ruoyi.common.core.domain.AjaxResult
import com.ruoyi.common.core.domain.AjaxResult.Companion.error
import com.ruoyi.common.exception.DemoModeException
import com.ruoyi.common.exception.ServiceException
import com.ruoyi.common.utils.MyStringUtils
import org.slf4j.LoggerFactory
import org.springframework.security.access.AccessDeniedException
import org.springframework.validation.BindException
import org.springframework.web.HttpRequestMethodNotSupportedException
import org.springframework.web.bind.MethodArgumentNotValidException
import org.springframework.web.bind.MissingPathVariableException
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.RestControllerAdvice
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException
import javax.servlet.http.HttpServletRequest

/**
 * 全局异常处理器
 *
 * @author ruoyi
 */
@RestControllerAdvice
class GlobalExceptionHandler {
    /**
     * 权限校验异常
     */
    @ExceptionHandler(AccessDeniedException::class)
    fun handleAccessDeniedException(e: AccessDeniedException, request: HttpServletRequest): AjaxResult {
        val requestURI = request.requestURI
        log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.message)
        return error(HttpStatus.FORBIDDEN, "没有权限，请联系管理员授权")
    }

    /**
     * 请求方式不支持
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException::class)
    fun handleHttpRequestMethodNotSupported(e: HttpRequestMethodNotSupportedException,
                                            request: HttpServletRequest): AjaxResult {
        val requestURI = request.requestURI
        log.error("请求地址'{}',不支持'{}'请求", requestURI, e.method)
        return error(e.message)
    }

    /**
     * 业务异常
     */
    @ExceptionHandler(ServiceException::class)
    fun handleServiceException(e: ServiceException, request: HttpServletRequest?): AjaxResult {
        log.error(e.message, e)
        val code: Int? = e.getCode()
        return if (MyStringUtils.isNotNull(code)) error(code!!, e.message) else error(e.message)
    }

    /**
     * 请求路径中缺少必需的路径变量
     */
    @ExceptionHandler(MissingPathVariableException::class)
    fun handleMissingPathVariableException(e: MissingPathVariableException, request: HttpServletRequest): AjaxResult {
        val requestURI = request.requestURI
        log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI, e)
        return error(String.format("请求路径中缺少必需的路径变量[%s]", e.variableName))
    }

    /**
     * 请求参数类型不匹配
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException::class)
    fun handleMethodArgumentTypeMismatchException(e: MethodArgumentTypeMismatchException, request: HttpServletRequest): AjaxResult {
        val requestURI = request.requestURI
        log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI, e)
        return error(String.format("请求参数类型不匹配，参数[%s]要求类型为：'%s'，但输入值为：'%s'", e.name, e.requiredType.name, e.value))
    }

    /**
     * 拦截未知的运行时异常
     */
    @ExceptionHandler(RuntimeException::class)
    fun handleRuntimeException(e: RuntimeException, request: HttpServletRequest): AjaxResult {
        val requestURI = request.requestURI
        log.error("请求地址'{}',发生未知异常.", requestURI, e)
        return error(e.message)
    }

    /**
     * 系统异常
     */
    @ExceptionHandler(Exception::class)
    fun handleException(e: Exception, request: HttpServletRequest): AjaxResult {
        val requestURI = request.requestURI
        log.error("请求地址'{}',发生系统异常.", requestURI, e)
        return error(e.message)
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(BindException::class)
    fun handleBindException(e: BindException): AjaxResult {
        log.error(e.message, e)
        val message = e.allErrors[0].defaultMessage
        return error(message)
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(MethodArgumentNotValidException::class)
    fun handleMethodArgumentNotValidException(e: MethodArgumentNotValidException): Any {
        log.error(e.message, e)
        val message = e.bindingResult.fieldError?.defaultMessage
        return error(message)
    }

    /**
     * 演示模式异常
     */
    @ExceptionHandler(DemoModeException::class)
    fun handleDemoModeException(e: DemoModeException?): AjaxResult {
        return error("演示模式，不允许操作")
    }

    companion object {
        private val log = LoggerFactory.getLogger(GlobalExceptionHandler::class.java)
    }
}
